package com.sunday.common.mq.rabbit.study.base.e1_HelloWorld;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.sunday.common.mq.rabbit.study.base.ConnectionUtil;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;

/**
 * Description: “Hello World！”
 * <p>
 * RabbitMQ说多种协议。本教程使用AMQP 0-9-1，它是一种开放的，通用的消息传递协议。RabbitMQ有许多不同语言的客户端。我们将使用RabbitMQ提供的Java客户端。
 * RabbitMQ 是一个消息代理：它接受并转发消息。 你可以把它想象成一个邮局：当你把你想要邮寄的邮件放在邮箱里时， 您可以确定，邮递员最终会将邮件递送给您的收件人。 在这个类比中，RabbitMQ是一个邮政信箱，一个邮局和一个信件载体。
 * <p>
 * RabbitMQ Java客户端使用com.rabbitmq.client作为其顶级包。关键类和接口是：
 * <p>
 * Channel：表示AMQP 0-9-1通道，并提供大部分操作（协议方法）。
 * Connection：表示AMQP 0-9-1连接
 * ConnectionFactory：构造Connection实例
 * Consumer：代表消息消费者
 * DefaultConsumer：消费者常用的基类
 * BasicProperties：消息属性（元数据）
 * BasicProperties.Builder：建设者BasicProperties
 * <p>
 * 协议操作可通过  Channel接口获得。连接用于打开通道，注册连接生命周期事件处理程序以及关闭不再需要的连接。
 * Connection通过ConnectionFactory实例化，这是您配置各种连接设置的方式，例如vhost或用户名。
 */
@Slf4j
public class Send {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {

        ConnectionFactory factory = ConnectionUtil.factory();

        // 我们可以使用 try-with-resources 语句 因为 Connection 和 Channel 都实现了 java.lang.AutoCloseable。 这样我们就不需要在代码中显式关闭它们
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {

            /**
             * 声明队列
             *
             * @see com.rabbitmq.client.AMQP.Queue.Declare
             * @see com.rabbitmq.client.AMQP.Queue.DeclareOk
             * @param queue            排队等待队列的名称
             * @param durable        如果我们声明一个持久的队列（队列在服务器重启后仍然存在），持久true。
             * @param exclusive        如果我们声明一个独占队列（仅限于此连接），独占true。
             * @param autoDelete    如果我们声明一个自动删除队列，autoDelete true（服务器将在不再使用时删除它,不会关注里面是否仍然存在数据）
             * @param arguments        参数为队列的其他属性（构造参数）
             * @return 声明确认方法，以表明已成功申报队列
             * @throws . io .如果遇到错误，IOException
             *
             * PS: RabbitMq在声明交换器和队列时，都有一个共同的属性为autoDelete，从字面意思上来说可以理解为自动删除，那么自动删除的前提是什么呢？
             * 抛开代码本身来说，从设计者的角度来思考，一个交换器或者一个队列在什么时候需要被自动删除呢？
             * 当然是该交换器或队列不被需要了，即交换器下曾经绑定过的交换器或队列解除绑定，队列下的消费者解除订阅。
             * 但是，一个交换器如果从来未曾被任何交换器/队列绑定过，一个队列从来未曾被消费者订阅过，这时候能否被自动删除呢？当然不行，因为如果想要删除某段时间内未被绑定或消费的交换器及队列，RabbitMq还有自动过期属性呢，总不能让autoDelete属性把两件事都干了吧。
             * 所以，总结一下，autoDelete属性针对的是曾经有过但后来没有的事物。
             *
             * Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments) throws IOException;
             */
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            /**
             * 发布消息
             *
             * 发布到一个不存在的交换将会导致一个通道级别 协议异常，它关闭通道。
             * 如果a/code的调用最终会被调用资源驱动的警报系统正在发挥作用，这是一种有效的方法。
             *
             * @see com.rabbitmq.client.AMQP.Basic.Publish 资源驱动的警报/信息：资源驱动的警报。
             * @param exchange        交换交换来发布消息
             * @param routingKey    路由键路由键
             * @param props            支持消息-路由标头等的其他属性
             * @param body            消息体
             * @throws . io .如果遇到错误，IOException
             *
             * void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
             */
            String message = "Hello World!";
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
            log.info(" [x] Sent '{}'", message);


        }
    }
}
